home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 / Ham Radio 2000.iso / ham2000 / tcp_ip / os2 / pmnos11s / smtpserv.c < prev    next >
C/C++ Source or Header  |  1993-07-30  |  26KB  |  1,085 lines

  1. /* SMTP Server state machine - see RFC 821
  2.  *  enhanced 4/88 Dave Trulli nn2z
  3.  *
  4.  * Mods by G1EMM and PA0GRI
  5.  */
  6. #include <stdio.h>
  7. #include <time.h>
  8. #if defined(PM)
  9. extern void MailNotify(char *pszUser);
  10. #endif
  11. #ifdef UNIX
  12. #include <sys/types.h>
  13. #endif
  14. #if    defined(__STDC__) || defined(__TURBOC__)
  15. #include <stdarg.h>
  16. #endif
  17. #include <ctype.h>
  18. #include <setjmp.h>
  19. #include "global.h"
  20. #include "config.h"
  21. #include "mbuf.h"
  22. #include "cmdparse.h"
  23. #include "socket.h"
  24. #ifdef    LZW
  25. #include "lzw.h"
  26. #endif
  27. #include "iface.h"
  28. #include "proc.h"
  29. #include "smtp.h"
  30. #include "commands.h"
  31. #include "dirutil.h"
  32. #include "mailbox.h"
  33. #include "bm.h"
  34. #include "domain.h"
  35. #include "session.h"
  36. #ifdef  NNTPS
  37. #include "nntp.h"
  38. #endif
  39.  
  40. char *Days[7] = {  "Sun","Mon","Tue","Wed","Thu","Fri","Sat" };
  41. char *Months[12] = { "Jan","Feb","Mar","Apr","May","Jun",
  42.         "Jul","Aug","Sep","Oct","Nov","Dec" };
  43.  
  44.  
  45.  
  46. static struct list *expandalias __ARGS((struct list **head,char *user));
  47. static int  getmsgtxt __ARGS((struct smtpsv *mp));
  48. static struct smtpsv *mail_create __ARGS((void));
  49. static void mail_clean __ARGS((struct smtpsv *mp));
  50. static int mailit __ARGS((FILE *data,char *from,struct list *tolist));
  51. static int router_queue __ARGS((FILE *data,char *from,struct list *to));
  52. static void smtplog __ARGS((char *fmt,...));
  53. static void smtpserv __ARGS((int s,void *unused,void *p));
  54. static int mailuser __ARGS((FILE *data,char *from,char *to));
  55.  
  56. /* Command table */
  57. static char *commands[] = {
  58.     "helo",
  59. #define    HELO_CMD    0
  60.     "noop",
  61. #define    NOOP_CMD    1
  62.     "mail from:",
  63. #define    MAIL_CMD    2
  64.     "quit",
  65. #define    QUIT_CMD    3
  66.     "rcpt to:",
  67. #define    RCPT_CMD    4
  68.     "help",
  69. #define    HELP_CMD    5
  70.     "data",
  71. #define    DATA_CMD    6
  72.     "rset",
  73. #define    RSET_CMD    7
  74.     "expn",
  75. #define EXPN_CMD    8
  76. #ifdef    LZW
  77.     "xlzw",
  78. #define XLZW_CMD    9
  79. #endif
  80.     NULLCHAR
  81. };
  82.  
  83. /* Reply messages */
  84. static char Banner[] = "220 %s SMTP ready\n";
  85. static char Closing[] = "221 Closing\n";
  86. static char Ok[] = "250 Ok\n";
  87. static char Reset[] = "250 Reset state\n";
  88. static char Sent[] = "250 Sent\n";
  89. static char Ourname[] = "250 %s, Share and Enjoy!\n";
  90. #ifdef    LZW
  91. static char LZWOk[] = "250 %d %d LZW Ok\n";
  92. static char Help[] = "214-Commands:\n214-HELO NOOP MAIL QUIT RCPT HELP DATA RSET EXPN XLZW\n214 End\n";
  93. #else
  94. static char Help[] = "214-Commands:\n214-HELO NOOP MAIL QUIT RCPT HELP DATA RSET EXPN\n214 End\n";
  95. #endif
  96. static char Enter[] = "354 Enter mail, end with .\n";
  97. static char Lowmem[] = "421 System overloaded, try again later\n";
  98. static char Ioerr[] = "452 Temp file write error\n";
  99. static char Badcmd[] = "500 Command unrecognized\n";
  100. static char Syntax[] = "501 Syntax error\n";
  101. static char Needrcpt[] = "503 Need RCPT (recipient)\n";
  102. static char Unknown[] = "550 <%s> address unknown\n";
  103. static char Noalias[] = "550 No alias for <%s>\n";
  104.  
  105. static int Ssmtp = -1; /* prototype socket for service */
  106.  
  107. #ifdef    MSDOS
  108. /* Valid characters in a DOS filename matrix */
  109. static unsigned char doschars[] = {
  110.     0x00, 0x00, 0x00, 0x00, 0xfa, 0x23, 0xff, 0x03,
  111.     0xff, 0xff, 0xff, 0xc7, 0xff, 0xff, 0xff, 0x6f,
  112.     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  113.     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
  114.     };
  115. static int dosfnchr __ARGS((int ch));
  116. #endif
  117.  
  118. /* Start up SMTP receiver service */
  119. int
  120. smtp1(argc,argv,p)
  121. int argc;
  122. char *argv[];
  123. void *p;
  124. {
  125.     struct sockaddr_in lsocket;
  126.     int s;
  127.  
  128.     if(Ssmtp != -1){
  129.         return 0;
  130.     }
  131.     psignal(Curproc,0);    /* Don't keep the parser waiting */
  132.     chname(Curproc,"SMTP listener");
  133.  
  134.     lsocket.sin_family = AF_INET;
  135.     lsocket.sin_addr.s_addr = INADDR_ANY;
  136.     if(argc < 2)
  137.         lsocket.sin_port = IPPORT_SMTP;
  138.     else
  139.         lsocket.sin_port = atoi(argv[1]);
  140.  
  141.     Ssmtp = socket(AF_INET,SOCK_STREAM,0);
  142.     bind(Ssmtp,(char *)&lsocket,sizeof(lsocket));
  143.     listen(Ssmtp,1);
  144.     for(;;){
  145.         if((s = accept(Ssmtp,NULLCHAR,(int *)NULL)) == -1)
  146.             break;    /* Service is shutting down */
  147.  
  148.         /* Low mem check now done in tcpin.c - WG7J */
  149.         /* Spawn a server */
  150.         newproc("SMTP server",2048,smtpserv,s,NULL,NULL,0);
  151.     }
  152.     return 0;
  153. }
  154.  
  155. /* Shutdown SMTP service (existing connections are allowed to finish) */
  156. int
  157. smtp0(argc,argv,p)
  158. int argc;
  159. char *argv[];
  160. void *p;
  161. {
  162.     close_s(Ssmtp);
  163.     Ssmtp = -1;
  164.     return 0;
  165. }
  166.  
  167. static void
  168. smtpserv(s,unused,p)
  169. int s;
  170. void *unused;
  171. void *p;
  172. {
  173.     struct smtpsv *mp;
  174.     char **cmdp,buf[LINELEN],*arg,*cp,*cmd,*newaddr;
  175.     struct list *ap,*list;
  176.     int cnt;
  177.     char address_type;
  178. #ifdef    LZW
  179.     extern int Smtpslzw;
  180.     int lzwbits, lzwmode;
  181. #endif
  182.  
  183.     sockmode(s,SOCK_ASCII);
  184.     sockowner(s,Curproc);        /* We own it now */
  185.     log(s,"open SMTP");
  186.  
  187.     if((mp = mail_create()) == NULLSMTPSV){
  188.         tprintf(Nospace);
  189.         tprintf(Nospace);
  190.         log(s,"close SMTP - no space");
  191.         close_s(s);
  192.         return;
  193.     }
  194.     mp->s = s;
  195.  
  196.     (void) usprintf(s,Banner,Hostname);
  197.  
  198. loop:    if ((cnt = recvline(s,buf,sizeof(buf))) == -1){
  199.         /* He closed on us */
  200.         goto quit;
  201.     }
  202.     if(cnt < 4){
  203.         /* Can't be a legal command */
  204.         usprintf(mp->s,Badcmd);
  205.         goto loop;
  206.     }    
  207.     rip(buf);
  208.     cmd = buf;
  209.  
  210.     /* Translate entire buffer to lower case */
  211.     for(cp = cmd;*cp != '\0';cp++)
  212.         *cp = tolower(*cp);
  213.  
  214.     /* Find command in table; if not present, return syntax error */
  215.     for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
  216.         if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
  217.             break;
  218.     if(*cmdp == NULLCHAR){
  219.         (void) usprintf(mp->s,Badcmd);
  220.         goto loop;
  221.     }
  222.     arg = &cmd[strlen(*cmdp)];
  223.     /* Skip spaces after command */
  224.     while(*arg == ' ')
  225.         arg++;
  226.     /* Execute specific command */
  227.     switch(cmdp-commands){
  228. #ifdef    LZW
  229.     case XLZW_CMD:
  230.         if(!Smtpslzw) {
  231.             usprintf(mp->s,Badcmd);
  232.         } else {
  233.             lzwmode = lzwbits = 0;
  234.             sscanf(arg,"%d %d",&lzwbits,&lzwmode);
  235.             if(!((lzwmode == 0 || lzwmode == 1)
  236.               && (lzwbits > 8 && lzwbits < 17))) {
  237.                 lzwmode = LZWCOMPACT;
  238.                 lzwbits = LZWBITS;
  239.                 usprintf(mp->s,Badcmd);
  240.             } else {
  241.                 usprintf(mp->s,LZWOk,lzwbits,lzwmode);
  242.                 lzwinit(mp->s,lzwbits,lzwmode);
  243.             }
  244.         }
  245.         break;
  246. #endif
  247.     case HELO_CMD:
  248.         free(mp->system);
  249.         mp->system = strdup(arg);
  250.         (void) usprintf(mp->s,Ourname,Hostname);
  251.         break;
  252.     case NOOP_CMD:
  253.         (void) usprintf(mp->s,Ok);
  254.         break;
  255.     case MAIL_CMD:
  256.         if((cp = getname(arg)) == NULLCHAR){
  257.             (void) usprintf(mp->s,Syntax);
  258.             break;
  259.         }
  260.         free(mp->from);
  261.         mp->from = strdup(cp);
  262.         (void) usprintf(mp->s,Ok);
  263.         break;
  264.     case QUIT_CMD:
  265.         (void) usprintf(mp->s,Closing);
  266.         goto quit;
  267.     case RCPT_CMD:    /* Specify recipient */
  268.         if((cp = getname(arg)) == NULLCHAR){
  269.             (void) usprintf(mp->s,Syntax);
  270.             break;
  271.         }
  272.         /* rewrite address if possible */
  273.         if((newaddr = rewrite_address(cp)) != NULLCHAR){
  274.             strcpy(buf,newaddr);
  275.             cp = buf;
  276.             free(newaddr);
  277.         }
  278.  
  279.         /* check if address is ok */
  280.         if ((address_type = validate_address(cp)) == BADADDR){
  281.             (void) usprintf(mp->s,Unknown,cp);
  282.             break;
  283.         }
  284.         /* if a local address check for an alias */
  285.         if (address_type == LOCAL)
  286.             expandalias(&mp->to, cp);
  287.         else
  288.             /* a remote address is added to the list */
  289.             addlist(&mp->to, cp, address_type);
  290.  
  291.         (void) usprintf(mp->s,Ok);
  292.         break;
  293.     case HELP_CMD:
  294.         (void) usprintf(mp->s,Help);
  295.         break;
  296.     case DATA_CMD:
  297.         if(mp->to == NULLLIST)
  298.             (void) usprintf(mp->s,Needrcpt);
  299.         else if ((mp->data = tmpfile()) == NULLFILE)
  300.             (void) usprintf(mp->s,Ioerr);
  301.          else
  302.             getmsgtxt(mp);
  303.         break;
  304.     case RSET_CMD:
  305.         del_list(mp->to);
  306.         mp->to = NULLLIST;
  307.         (void) usprintf(mp->s,Reset);
  308.         break;
  309.     case EXPN_CMD:
  310.         if (*arg == '\0'){
  311.             (void) usprintf(mp->s,Syntax);
  312.             break;
  313.         }
  314.  
  315.         list = NULLLIST;
  316.         /* rewrite address if possible */
  317.         if((newaddr = rewrite_address(arg)) != NULLCHAR)
  318.             if(strcmp(newaddr,arg) == 0){
  319.                 free(newaddr);
  320.                 newaddr = NULLCHAR;
  321.             } else {
  322.                 strcpy(buf,newaddr);
  323.                 arg = buf;
  324.             }
  325.         list = NULLLIST;
  326.         expandalias(&list,arg);
  327.         if (strcmp(list->val,arg) == 0 && list->next == NULLLIST)
  328.             if(newaddr == NULLCHAR){
  329.                 (void) usprintf(mp->s,Noalias,arg);
  330.                 del_list(list);
  331.                 break;
  332.             }
  333.         ap = list;
  334.         while (ap->next != NULLLIST){
  335.             (void) usprintf(mp->s,"250-%s\n",ap->val);
  336.             ap = ap->next;
  337.         }
  338.         usprintf(mp->s,"250 %s\n",ap->val);
  339.         del_list(list);
  340.         free(newaddr);
  341.         break;
  342.     }
  343.     goto loop;
  344.  
  345. quit:
  346.     log(mp->s,"close SMTP");
  347.     close_s(mp->s);
  348.     mail_clean(mp);
  349.     smtptick(NULL);            /* start SMTP daemon immediately */
  350. }
  351.  
  352. extern char shortversion[];
  353. extern char *Mbfwdinfo;
  354.  
  355. /* read the message text */
  356. static int
  357. getmsgtxt(mp)
  358. struct smtpsv *mp;
  359. {
  360.     char buf[LINELEN];
  361.     register char *p = buf;
  362.     long t;
  363.  
  364.     /* Add timestamp; ptime adds newline */
  365.     time(&t);
  366.     fprintf(mp->data,"Received: ");
  367.     if(mp->system != NULLCHAR)
  368.         fprintf(mp->data,"from %s ",mp->system);
  369. #ifdef MBFWD
  370.     fprintf(mp->data,"by %s (%s) with SMTP\n\tid AA%ld ; %s",
  371.             Hostname, (Mbfwdinfo != NULLCHAR) ? Mbfwdinfo : shortversion, \
  372.             get_msgid(), ptime(&t));
  373. #else
  374.     fprintf(mp->data,"by %s (%s) with SMTP\n\tid AA%ld ; %s",
  375.             Hostname, shortversion, get_msgid(), ptime(&t));
  376. #endif
  377.     if(ferror(mp->data)){
  378.         (void) usprintf(mp->s,Ioerr);
  379.         return 1;
  380.     } else {
  381.         (void) usprintf(mp->s,Enter);
  382.     }
  383.     while(1){
  384.         if(recvline(mp->s,p,sizeof(buf)) == -1){
  385.             return 1;
  386.         }
  387.         rip(p);
  388.         /* check for end of message ie a . or escaped .. */
  389.         if (*p == '.'){
  390.             if (*++p == '\0'){
  391.                 /* Also sends appropriate response */
  392.                 if (mailit(mp->data,mp->from,mp->to) != 0)
  393.                     (void) usprintf(mp->s,Ioerr);
  394.                 else
  395.                     (void) usprintf(mp->s,Sent);
  396.                 fclose(mp->data);
  397.                 mp->data = NULLFILE;
  398.                 del_list(mp->to);
  399.                 mp->to = NULLLIST;
  400.                 return 0;
  401.             } else if (!(*p == '.' && *(p+1) == '\0'))
  402.                 p--;
  403.         }
  404.         /* for UNIX mail compatiblity */
  405.         if (strncmp(p,"From ",5) == 0)
  406.             (void) putc('>',mp->data);
  407.         /* Append to data file */
  408.         if(fprintf(mp->data,"%s\n",p) < 0){
  409.             (void) usprintf(mp->s,Ioerr);
  410.             return 1;
  411.         }
  412.     }
  413. }
  414.  
  415. /* Create control block, initialize */
  416. static struct smtpsv *
  417. mail_create()
  418. {
  419.     register struct smtpsv *mp;
  420.  
  421.     mp = (struct smtpsv *)callocw(1,sizeof(struct smtpsv));
  422.     mp->from = strdup("");    /* Default to null From address */
  423.     return mp;
  424. }
  425.  
  426. /* Free resources, delete control block */
  427. static void
  428. mail_clean(mp)
  429. register struct smtpsv *mp;
  430. {
  431.     if (mp == NULLSMTPSV)
  432.         return;
  433.     free(mp->system);
  434.     free(mp->from);
  435.     if(mp->data != NULLFILE)
  436.         fclose(mp->data);
  437.     del_list(mp->to);
  438.     free((char *)mp);
  439. }
  440.  
  441. /* General mailit function. It takes a list of addresses which have already
  442. ** been verified and expanded for aliases. Base on the current mode the message
  443. ** is place in an mbox, the outbound smtp queue or the rqueue interface
  444. */
  445. static int
  446. mailit(data,from,tolist)
  447. FILE *data;
  448. char *from;
  449. struct list *tolist;
  450. {
  451.     struct list *ap, *dlist = NULLLIST;
  452.     register FILE *fp;
  453.     char    mailbox[50], *cp, *host, *qhost;
  454.     int    c, fail = 0;
  455.     time_t    t;
  456.     extern int Smtpquiet;
  457.  
  458.     if ((Smtpmode & QUEUE) != 0)
  459.         return(router_queue(data,from,tolist));
  460.  
  461.     do {
  462.         qhost = NULLCHAR;
  463.         for(ap = tolist;ap != NULLLIST;ap = ap->next)
  464.             if (ap->type == DOMAIN){
  465.                 if ((host = strrchr(ap->val,'@')) != NULLCHAR)
  466.                     host++;
  467.                 else
  468.                     host = Hostname;
  469.                 if(qhost == NULLCHAR)
  470.                          qhost = host;
  471.                 if(stricmp(qhost,host) == 0){
  472.                     ap->type = BADADDR;
  473.                     addlist(&dlist,ap->val,0);
  474.                 }
  475.             }
  476.         if(qhost != NULLCHAR){
  477.             rewind(data);
  478.             queuejob(data,qhost,dlist,from);
  479.             del_list(dlist);
  480.             dlist = NULLLIST;
  481.         }
  482.     } while(qhost != NULLCHAR);
  483.  
  484. #ifdef    NNTPS
  485.     for(ap = tolist;ap != NULLLIST;ap = ap->next){
  486.         if(ap->type != NNTP_GATE)
  487.             continue;
  488.         nnGpost(data,from,ap);
  489.         ap->type = BADADDR;
  490.     }
  491. #endif
  492.  
  493.     for(ap = tolist;ap != NULLLIST;ap = ap->next){
  494.         if(ap->type != LOCAL){
  495.             ap->type = DOMAIN;
  496.             continue;
  497.         }
  498.         rewind(data);
  499.         /* strip off host name of LOCAL addresses */
  500.         if ((cp = strchr(ap->val,'@')) != NULLCHAR)
  501.             *cp = '\0';
  502.  
  503.         /* truncate long user names */
  504.         if (strlen(ap->val) > MBOXLEN)
  505.             ap->val[MBOXLEN] = '\0';
  506.  
  507.         /* if mail file is busy save it in our smtp queue
  508.          * and let the smtp daemon try later.
  509.          */
  510.         if (mlock(Mailspool,ap->val)){
  511.             addlist(&dlist,ap->val,0);
  512.             fail = queuejob(data,Hostname,dlist,from);
  513.             del_list(dlist);
  514.             dlist = NULLLIST;
  515.         } else {
  516.             char buf[LINELEN];
  517.             int tocnt = 0;
  518.             sprintf(mailbox,"%s/%s.txt",Mailspool,ap->val);
  519. #ifndef    AMIGA
  520.             if((fp = fopen(mailbox,APPEND_TEXT)) != NULLFILE){
  521. #else
  522.             if((fp = fopen(mailbox,"r+")) != NULLFILE){
  523.                 (void) fseek(fp, 0L, 2);
  524. #endif
  525.                 time(&t);
  526.                 fprintf(fp,"From %s %s",from,ctime(&t));
  527.                 host = NULLCHAR;
  528.                 while(fgets(buf,sizeof(buf),data) != NULLCHAR){
  529.                     if(buf[0] == '\n'){
  530.                         if(tocnt == 0)
  531.                             fprintf(fp,"%s%s\n",
  532.                                 Hdrs[APPARTO],
  533.                                 ap->val);
  534.                         fputc('\n',fp);
  535.                         break;
  536.                     }
  537.                     fputs(buf,fp);
  538.                     rip(buf);
  539.                     switch(htype(buf)){
  540.                     case TO:
  541.                     case CC:
  542.                         ++tocnt;
  543.                         break;
  544.                     case RRECEIPT:
  545.                         if((cp = getaddress(buf,0))
  546.                            != NULLCHAR){
  547.                             free(host);
  548.                             host = strdup(cp);
  549.                         }
  550.                         break;
  551.                     }
  552.                 }
  553.                 while((c = fread(buf,1,sizeof(buf),data)) > 0)
  554.                     if(fwrite(buf,1,c,fp) != c)
  555.                         break;
  556.                 if(ferror(fp))
  557.                     fail = 1;
  558.                 else
  559.                     fprintf(fp,"\n");
  560.                 /* Leave a blank line between msgs */
  561.                 fclose(fp);
  562. /* If we use tprintf here, instead of printf, flowcontrol
  563.  * in the command screen is used; if the system is unattended for
  564.  * more then 24 messages coming in, it will lock up mail delivery.
  565.  * Make sure this only goes to the command screen - WG7J
  566.  */
  567.                 if(Current->output == Command->output)
  568. #if !defined(PM)
  569.                     printf("New mail for %s from <%s>%c\n",ap->val,from, Smtpquiet ? ' ' : '\007');
  570.  
  571. #else
  572.                     tprintf("New mail arrived for %s from <%s>%c\n",ap->val,from, Smtpquiet ? ' ' : '\007');
  573.                 MailNotify(ap->val);
  574. #endif
  575.                 if(host != NULLCHAR){
  576.                     rewind(data); /* Send return receipt */
  577.                     mdaemon(data,host,NULLLIST,0);
  578.                     free(host);
  579.                 }
  580.             } else
  581.                 fail = 1;
  582.             (void) rmlock(Mailspool,ap->val);
  583.             if (fail)
  584.                 break;
  585.             smtplog("deliver: To: %s From: %s",ap->val,from);
  586.         }
  587.     }
  588.     return fail;
  589. }
  590.  
  591. /* Return Date/Time in Arpanet format in passed string */
  592. char *
  593. ptime(t)
  594. long *t;
  595. {
  596.     /* Print out the time and date field as
  597.      *        "DAY day MONTH year hh:mm:ss ZONE"
  598.      */
  599.     register struct tm *ltm;
  600.     static char tz[4];
  601.     static char str[40];
  602.     char *p, *getenv();
  603.     /* Read the system time */
  604.     ltm = localtime(t);
  605.  
  606.     if (*tz == '\0')
  607.         if ((p = getenv("TZ")) == NULL)
  608.             strcpy(tz,"UTC");
  609.         else
  610.             strncpy(tz,p,3);
  611.  
  612.     /* rfc 822 format */
  613.     sprintf(str,"%s, %.2d %s %02d %02d:%02d:%02d %.3s\n",
  614.         Days[ltm->tm_wday],
  615.         ltm->tm_mday,
  616.         Months[ltm->tm_mon],
  617.         ltm->tm_year,
  618.         ltm->tm_hour,
  619.         ltm->tm_min,
  620.         ltm->tm_sec,
  621.         tz);
  622.     return(str);
  623. }
  624.  
  625. long
  626. get_msgid()
  627. {
  628.     char sfilename[LINELEN];
  629.     char s[20];
  630.     register long sequence = 0;
  631.     FILE *sfile;
  632.  
  633.     sprintf(sfilename,"%s/sequence.seq",Mailqdir);
  634.     sfile = fopen(sfilename,READ_TEXT);
  635.  
  636.     /* if sequence file exists, get the value, otherwise set it */
  637.     if (sfile != NULL){
  638.         (void) fgets(s,sizeof(s),sfile);
  639.         sequence = atol(s);
  640.     /* Keep it in range of and 8 digit number to use for dos name prefix. */
  641.         if (sequence < 0L || sequence > 99999999L )
  642.             sequence = 0;
  643.         fclose(sfile);
  644.     }
  645.  
  646.     /* increment sequence number, and write to sequence file */
  647.     sfile = fopen(sfilename,WRITE_TEXT);
  648.     fprintf(sfile,"%ld",++sequence);
  649.     fclose(sfile);
  650.     return sequence;
  651. }
  652.  
  653. /* test if mail address is valid */
  654. int
  655. validate_address(s)
  656. char *s;
  657. {
  658.     char *cp;
  659.     int32 addr;
  660.     int i;
  661.  
  662.     if(*s == '!'){
  663. #ifdef    NNTPS
  664.         if ((cp = strpbrk(s,"%@.,/")) != NULLCHAR)
  665.             *cp = '\0';
  666.         return NNTP_GATE;
  667. #else
  668.         return BADADDR;
  669. #endif
  670.     }
  671.     /* if address has @ in it then check dest address */
  672.     if ((cp = strrchr(s,'@')) != NULLCHAR){
  673.         cp++;
  674.         /* 1st check if it is our hostname.
  675.         * if not then check the hosts file and see if we can
  676.         * resolve the address to a know site or one of our aliases.
  677.         */
  678.         if(stricmp(cp,Hostname) != 0){
  679.             if ((addr = mailroute(cp)) == 0
  680.                 && (Smtpmode & QUEUE) == 0)
  681.                 return BADADDR;
  682.             if (ismyaddr(addr) == NULLIF)
  683.                 return DOMAIN;
  684.         }
  685.         
  686.         /* on a local address remove the host name part */
  687.         *--cp = '\0';
  688.     }
  689.  
  690.     /* if using an external router leave address alone */
  691.     if ((Smtpmode & QUEUE) != 0)
  692.         return LOCAL;
  693.  
  694.     /* check for the user%host hack */
  695.     if ((cp = strrchr(s,'%')) != NULLCHAR){
  696.         *cp = '@';
  697.         cp++;
  698.         /* reroute based on host name following the % seperator */
  699.         if (mailroute(cp) == 0)
  700.             return BADADDR;
  701.         else
  702.             return DOMAIN;
  703.     }
  704.  
  705. #ifdef MSDOS    /* dos file name checks */
  706.     /* Check for characters illegal in MS-DOS file names */
  707.     for(cp = s;*cp != '\0';cp++){
  708.         if(dosfnchr(*cp) == 0){
  709.             return BADADDR;    
  710.         }
  711.     }
  712. #endif
  713.     return LOCAL;
  714. }
  715.  
  716. /* place a mail job in the outbound queue */
  717. int
  718. queuejob(dfile,host,to,from)
  719. FILE *dfile;
  720. char *host;
  721. struct list *to;
  722. char *from;
  723. {
  724.     FILE *fp;
  725.     struct list *ap;
  726.     char tmpstring[50], prefix[9], buf[LINELEN];
  727.     register int cnt;
  728.  
  729.     sprintf(prefix,"%ld",get_msgid());
  730.     mlock(Mailqdir,prefix);
  731.     sprintf(tmpstring,"%s/%s.txt",Mailqdir,prefix);
  732.     if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE){
  733.         (void) rmlock(Mailqdir,prefix);
  734.         return 1;
  735.     }
  736.     while((cnt = fread(buf, 1, LINELEN, dfile)) > 0)
  737.         if(fwrite(buf, 1, cnt, fp) != cnt)
  738.             break;
  739.     if(ferror(fp)){
  740.         fclose(fp);
  741.         (void) rmlock(Mailqdir,prefix);
  742.         return 1;
  743.     }
  744.     fclose(fp);
  745.     sprintf(tmpstring,"%s/%s.wrk",Mailqdir,prefix);
  746.     if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE){
  747.         (void) rmlock(Mailqdir,prefix);
  748.         return 1;
  749.     }
  750.     fprintf(fp,"%s\n%s\n",host,from);
  751.     for(ap = to; ap != NULLLIST; ap = ap->next){
  752.         fprintf(fp,"%s\n",ap->val);
  753.         smtplog("queue job %s To: %s From: %s",prefix,ap->val,from);
  754.     }
  755.     fclose(fp);
  756.     (void) rmlock(Mailqdir,prefix);
  757.     return 0;
  758. }
  759.  
  760. /* Deliver mail to the appropriate mail boxes */
  761. static int
  762. router_queue(data,from,to)
  763. FILE *data;
  764. char *from;
  765. struct list *to;
  766. {
  767.     int c;
  768.     register struct list *ap;
  769.     FILE *fp;
  770.     char tmpstring[50];
  771.     char prefix[9];
  772.  
  773.     sprintf(prefix,"%ld",get_msgid());
  774.     mlock(Routeqdir,prefix);
  775.     sprintf(tmpstring,"%s/%s.txt",Routeqdir,prefix);
  776.     if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE){
  777.         (void) rmlock(Routeqdir,prefix);
  778.         return 1;
  779.     }
  780.     rewind(data);
  781.     while((c = getc(data)) != EOF)
  782.         if(putc(c,fp) == EOF)
  783.             break;
  784.     if(ferror(fp)){
  785.         fclose(fp);
  786.         (void) rmlock(Routeqdir,prefix);
  787.         return 1;
  788.     }
  789.     fclose(fp);
  790.     sprintf(tmpstring,"%s/%s.wrk",Routeqdir,prefix);
  791.     if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE){
  792.         (void) rmlock(Routeqdir,prefix);
  793.         return 1;
  794.     }
  795.     fprintf(fp,"From: %s\n",from);
  796.     for(ap = to;ap != NULLLIST;ap = ap->next){
  797.         fprintf(fp,"To: %s\n",ap->val);
  798.     }
  799.     fclose(fp);
  800.     (void) rmlock(Routeqdir,prefix);
  801.     smtplog("rqueue job %s From: %s",prefix,from);
  802.     return 0;
  803. }
  804.  
  805. /* add an element to the front of the list pointed to by head
  806. ** return NULLLIST if out of memory.
  807. */
  808. struct list *
  809. addlist(head,val,type)
  810. struct list **head;
  811. char *val;
  812. int type;
  813. {
  814.     register struct list *tp;
  815.  
  816.     tp = (struct list *)callocw(1,sizeof(struct list));
  817.  
  818.     tp->next = NULLLIST;
  819.  
  820.     /* allocate storage for the char string */
  821.     tp->val = strdup(val);
  822.     tp->type = type;
  823.  
  824.     /* add entry to front of existing list */
  825.     if (*head == NULLLIST)
  826.         *head = tp;
  827.     else {
  828.         tp->next = *head;
  829.         *head = tp;
  830.     }
  831.     return tp;
  832.  
  833. }
  834.  
  835. #define SKIPWORD(X) while(*X && *X!=' ' && *X!='\t' && *X!='\n' && *X!= ',') X++;
  836. #define SKIPSPACE(X) while(*X ==' ' || *X =='\t' || *X =='\n' || *X == ',') X++;
  837.  
  838. /* check for and alias and expand alias into a address list */
  839. static struct list *
  840. expandalias(head, user)
  841. struct list **head;
  842. char *user;
  843. {
  844.     FILE *fp;
  845.     register char *s,*p;
  846.     struct rr *rrp, *rrlp;
  847.     int inalias = 0;
  848.     struct list *tp;
  849.     char buf[LINELEN];
  850.     
  851.     /* no alias file found */
  852.     if ((fp = fopen(Alias, READ_TEXT)) == NULLFILE){
  853.         /* Try MB, MG or MR domain name records */
  854.         rrlp = rrp = resolve_mailb(user);
  855.         while(rrp != NULLRR){
  856.             if(rrp->rdlength > 0){
  857.                 /* remove the trailing dot */
  858.                 rrp->rdata.name[rrp->rdlength-1] = '\0';
  859.                 /* replace first dot with @ if there is no @ */
  860.                 if(strchr(rrp->rdata.name,'@') == NULLCHAR
  861.                    && (p = strchr(rrp->rdata.name,'.')) !=
  862.                    NULLCHAR)
  863.                     *p = '@';
  864.                 if(strchr(rrp->rdata.name,'@') != NULLCHAR)
  865.                     tp = addlist(head,rrp->rdata.name,
  866.                              DOMAIN);
  867.                 else
  868.                     tp = addlist(head,rrp->rdata.name,
  869.                              LOCAL);
  870.                 ++inalias;
  871.             }
  872.             rrp = rrp->next;
  873.         }
  874.         free_rr(rrlp);
  875.         if(inalias)
  876.             return tp;
  877.         else
  878.             return addlist(head, user, LOCAL);
  879.     }
  880.  
  881.     while (fgets(buf,LINELEN,fp) != NULLCHAR){
  882.         p = buf;
  883.         if ( *p == '#' || *p == '\0')
  884.             continue;
  885.         rip(p);
  886.  
  887.         /* if not in an matching entry skip continuation lines */
  888.         if (!inalias && isspace(*p))
  889.             continue;
  890.  
  891.         /* when processing an active alias check for a continuation */
  892.         if (inalias){
  893.             if (!isspace(*p))
  894.                 break;    /* done */
  895.         } else {
  896.             s = p;
  897.             SKIPWORD(p);
  898.             *p++ = '\0';    /* end the alias name */
  899.             if (strcmp(s,user) != 0)
  900.                 continue;    /* no match go on */
  901.             inalias = 1;
  902.         }
  903.  
  904.         /* process the recipients on the alias line */
  905.         SKIPSPACE(p);
  906.         while(*p != '\0' && *p != '#'){
  907.             s = p;
  908.             SKIPWORD(p);
  909.             if (*p != '\0')
  910.                 *p++ = '\0';
  911.  
  912.             /* find hostname */
  913. #ifdef    NNTPS
  914.             if(*s == '!')
  915.                 tp = addlist(head,s,NNTP_GATE);
  916.             else
  917. #endif
  918.             if (strchr(s,'@') != NULLCHAR)
  919.                 tp = addlist(head,s,DOMAIN);
  920.             else
  921.                 tp = addlist(head,s,LOCAL);
  922.             SKIPSPACE(p);
  923.         }
  924.     }
  925.     (void) fclose(fp);
  926.  
  927.     if (inalias)    /* found and processed and alias. */
  928.         return tp;
  929.  
  930.     /* no alias found treat as a local address */
  931.     return addlist(head, user, LOCAL);
  932. }
  933.  
  934. #if    defined(ANSIPROTO)
  935. static void
  936. smtplog(char *fmt, ...)
  937. {
  938.     va_list ap;
  939.     char *cp;
  940.     long t;
  941.     FILE *fp;
  942.  
  943.     if ((fp = fopen(Maillog,APPEND_TEXT)) == NULLFILE)
  944.         return;
  945.     time(&t);
  946.     cp = ctime(&t);
  947.     rip(cp);
  948.     fprintf(fp,"%s ",cp);
  949.     va_start(ap,fmt);
  950.     vfprintf(fp,fmt,ap);
  951.     va_end(ap);
  952.     fprintf(fp,"\n");
  953.     fclose(fp);
  954. }
  955.  
  956. #else
  957.  
  958. static void
  959. smtplog(fmt,arg1,arg2,arg3,arg4)
  960. char *fmt;
  961. int arg1,arg2,arg3,arg4;
  962. {
  963.     char *cp;
  964.     long t;
  965.     FILE *fp;
  966.  
  967.     if ((fp = fopen(Maillog,APPEND_TEXT)) == NULLFILE)
  968.         return;
  969.     time(&t);
  970.     cp = ctime(&t);
  971.     rip(cp);
  972.     fprintf(fp,"%s ",cp);
  973.     fprintf(fp,fmt,arg1,arg2,arg3,arg4);
  974.     fprintf(fp,"\n");
  975.     fclose(fp);
  976. }
  977. #endif
  978.  
  979. /* send mail to a single user. Can be called from the ax25 mailbox or
  980. ** from the return mail function in the smtp client
  981. */
  982. static int
  983. mailuser(data,from,to)
  984. FILE *data;
  985. char *from;
  986. char *to;
  987. {
  988.  
  989.         int address_type, ret;
  990.         struct list *tolist = NULLLIST;
  991.  
  992.         /* check if address is ok */
  993.         if ((address_type = validate_address(to)) == BADADDR){
  994.             return 1;
  995.         }
  996.         /* if a local address check for an alias */
  997.         if (address_type == LOCAL)
  998.             expandalias(&tolist, to);
  999.         else
  1000.             /* a remote address is added to the list */
  1001.             addlist(&tolist, to, address_type);
  1002.         ret = mailit(data,from,tolist);
  1003.         del_list(tolist);
  1004.         return ret;
  1005.  
  1006. }
  1007.  
  1008. /* Mailer daemon return mail mechanism */
  1009. int
  1010. mdaemon(data,to,lp,bounce)
  1011. FILE *data;        /* pointer to rewound data file */
  1012. char *to;        /* Overridden by Errors-To: line if bounce is true */
  1013. struct list *lp;    /* error log for failed mail */
  1014. int bounce;        /* True for failed mail, otherwise return receipt */
  1015. {
  1016.     time_t t;
  1017.     FILE *tfile;
  1018.     char buf[LINELEN], *cp, *newto = NULLCHAR;
  1019.     int cnt;
  1020.     if(to == NULLCHAR || (to != NULLCHAR && *to == '\0') || bounce){
  1021.         while(fgets(buf,sizeof(buf),data) != NULLCHAR){
  1022.             if(buf[0] == '\n')
  1023.                 break;
  1024.             /* Look for Errors-To: */
  1025.             if(htype(buf) == ERRORSTO &&
  1026.                (cp = getaddress(buf,0)) != NULLCHAR){
  1027.                 free(newto);
  1028.                 newto = strdup(cp);
  1029.                 break;
  1030.             }
  1031.         }
  1032.         if(newto == NULLCHAR && ((to != NULLCHAR && *to == '\0') ||
  1033.            to == NULLCHAR))
  1034.             return -1;
  1035.         rewind(data);
  1036.     }
  1037.     if((tfile = tmpfile()) == NULLFILE)
  1038.         return -1;
  1039.     time(&t);
  1040.     fprintf(tfile,"%s%s",Hdrs[DATE],ptime(&t));
  1041.     fprintf(tfile,"%s<%ld@%s>\n",Hdrs[MSGID],get_msgid(),Hostname);
  1042.     fprintf(tfile,"%sMAILER-DAEMON@%s (Mail Delivery Subsystem)\n",
  1043.         Hdrs[FROM],Hostname);
  1044.     fprintf(tfile,"%s%s\n",Hdrs[TO],newto != NULLCHAR ? newto : to);
  1045.     fprintf(tfile,"%s%s\n\n",Hdrs[SUBJECT],
  1046.         bounce ? "Failed mail" : "Return receipt");
  1047.     if(bounce){
  1048.         fprintf(tfile,"  ===== transcript follows =====\n\n");
  1049.         for (; lp != NULLLIST; lp = lp->next)
  1050.             fprintf(tfile,"%s\n",lp->val);
  1051.         fprintf(tfile,"\n");
  1052.     }
  1053.     fprintf(tfile,"  ===== %s follows ====\n",
  1054.         bounce ? "Unsent message" : "Message header");
  1055.  
  1056.     while(fgets(buf,sizeof(buf),data) != NULLCHAR){
  1057.         if(buf[0] == '\n')
  1058.             break;
  1059.         fputs(buf,tfile);
  1060.     }
  1061.     if(bounce){
  1062.         fputc('\n',tfile);
  1063.         while((cnt = fread(buf,1,sizeof(buf),data)) > 0)
  1064.             fwrite(buf,1,cnt,tfile);
  1065.     }
  1066.     fseek(tfile,0L,0);
  1067.     /* A null From<> so no looping replys to MAIL-DAEMONS */
  1068.     (void) mailuser(tfile,"",newto != NULLCHAR ? newto : to);
  1069.     fclose(tfile);
  1070.     free(newto);
  1071.     return 0;
  1072. }
  1073. #ifdef MSDOS
  1074. static int
  1075. dosfnchr(ch)
  1076. int ch;
  1077. {
  1078.     int i, j;
  1079.  
  1080.     i = (ch & 0xf8) >> 3;
  1081.     j = doschars[i] & (1 << (ch & 0x07));
  1082.     return j;
  1083. }
  1084. #endif
  1085.